package com.hx.rpc.registry;

import com.hx.rpc.protocol.InvokerProtocol;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Huathy
 * @date 2021-10-23 15:01
 * @description 注册器
 * 1. 根据包名，将所有符合条件的class全部扫描出来，放到一个容器中。（如果是分布式，就是读配置文件）
 * 2. 给每一个对应的class起一个唯一的名字，作为服务名称。保存到容器中。
 * 3. 当有客户端连接之后，netty会自动解析，得到InvokerProtocol（协议内容）对象
 * 4. 要去注册好的容器中找到符合条件的服务
 * 5. 通过远程调用Provider得到返回结果，并回复给客户端。
 */
public class RegistryHandler extends ChannelInboundHandlerAdapter {

    private List<String> classNames = new ArrayList<>();
    private Map<String, Object> registryMap = new ConcurrentHashMap<>();

    public RegistryHandler(){
        // 1. 扫描class类，放入容器
        scannerClass("com.hx.rpc.provider");
        // 2. 给对应的class起一个唯一name，保存到容器中
        doRegistry();

    }

    /**
     * 注册
     */
    private void doRegistry() {
        if(classNames.isEmpty()){
            return;
        }
        for (String className : classNames) {
            try {
                Class<?> cls = Class.forName(className);
                Class<?> interfaceClass = cls.getInterfaces()[0];
                String serviceName = interfaceClass.getName();
                //这里本应该存放一个网络地址。从配置文件中读取，在调用时解析。
                registryMap.put(serviceName,cls.newInstance());

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 扫描Class。简化版本。正常的是读取配置文件获取。
     * @param packageName
     */
    private void scannerClass(String packageName) {
        URL url = this.getClass().getClassLoader().getResource(packageName.replaceAll("\\.", "/"));
        File classPath = new File(url.getFile());
        for (File file : classPath.listFiles()) {
            if(file.isDirectory()){
                scannerClass(packageName + "." + file.getName());
            }else {
                classNames.add(packageName + "." + file.getName().replace(".class",""));
            }
        }
    }

    /**
     * 有客户端连上的时候就会回调
     * @param ctx
     * @param msg
     * @throws Exception
     *  * 3. 当有客户端连接之后，netty会自动解析，得到InvokerProtocol（协议内容）对象
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        InvokerProtocol request = (InvokerProtocol) msg;
        Object result = "No method found";
        if(registryMap.containsKey(request.getClassName())){
            Object service = registryMap.get(request.getClassName());
            Method method = service.getClass().getMethod(request.getMethodName(), request.getParams());
            result = method.invoke(service, request.getValues());
        }
        ctx.write(result);
        ctx.flush();
        ctx.close();
        System.out.println("[log info] == > service return ：" + result);
    }

    /**
     * 连接发生异常的时候进行回调
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
